"""
工作流模板服务层
处理工作流模板的业务逻辑，包括版本管理
"""
import json
from typing import List, Optional, Tuple, Union
from datetime import datetime
from sqlalchemy.orm import Session
from sqlalchemy import desc, asc, func

from models.workflow_template import WorkflowTemplate
from models.workflow_instance import WorkflowInstance
from schemas.workflow_template_schema import (
    WorkflowTemplateCreate,
    WorkflowTemplateUpdate,
    WorkflowDefinition,
    VersionInfo
)


class WorkflowTemplateService:
    """工作流模板服务"""

    @staticmethod
    def create_template(
        db: Session,
        template_create: WorkflowTemplateCreate
    ) -> Tuple[bool, str, Optional[WorkflowTemplate]]:
        """
        创建工作流模板

        Args:
            db: 数据库会话
            template_create: 创建模板的请求数据

        Returns:
            (是否成功, 错误信息, 模板对象)
        """
        try:
            # 检查名称是否重复
            existing = db.query(WorkflowTemplate).filter(
                WorkflowTemplate.name == template_create.name,
                WorkflowTemplate.deleted_at.is_(None)
            ).first()

            if existing:
                return False, f"模板名称 '{template_create.name}' 已存在", None

            # 验证工作流定义
            template_create = _rebuild_workflow_definition(template_create)
            workflow_def = template_create.workflow_definition.model_dump()
            nodes = workflow_def.get("nodes", [])

            if not nodes:
                return False, "工作流必须至少包含一个节点", None

            # 验证节点ID唯一性
            node_ids = [node.get("id") for node in nodes]
            if len(node_ids) != len(set(node_ids)):
                return False, "节点ID必须唯一", None

            # 创建模板（版本1）
            template = WorkflowTemplate(
                name=template_create.name,
                description=template_create.description,
                workflow_definition=json.dumps(workflow_def, ensure_ascii=False),
                version=1,
                parent_version_id=None,
                change_log="初始版本",
                status="草稿",
                created_by=template_create.created_by
            )

            db.add(template)
            db.commit()
            db.refresh(template)

            return True, "", template

        except Exception as e:
            db.rollback()
            return False, f"创建模板失败: {str(e)}", None

    @staticmethod
    def get_template(db: Session, template_id: int) -> Optional[WorkflowTemplate]:
        """
        获取模板详情

        Args:
            db: 数据库会话
            template_id: 模板ID

        Returns:
            模板对象
        """
        return db.query(WorkflowTemplate).filter(
            WorkflowTemplate.id == template_id,
            WorkflowTemplate.deleted_at.is_(None)
        ).first()

    @staticmethod
    def list_templates(
        db: Session,
        page: int = 1,
        page_size: int = 20,
        name: Optional[str] = None,
        status: Optional[str] = None,
        sort_by: str = "created_at",
        order: str = "desc"
    ) -> Tuple[int, List[WorkflowTemplate]]:
        """
        查询模板列表（每个名称只返回最新版本）

        Args:
            db: 数据库会话
            page: 页码
            page_size: 每页数量
            name: 模板名称(模糊查询)
            status: 模板状态
            sort_by: 排序字段
            order: 排序方向

        Returns:
            (总数, 模板列表)
        """
        # 子查询：获取每个模板名称的最新版本
        subquery = db.query(
            WorkflowTemplate.name,
            func.max(WorkflowTemplate.version).label('max_version')
        ).filter(
            WorkflowTemplate.deleted_at.is_(None)
        ).group_by(WorkflowTemplate.name).subquery()

        # 主查询：根据名称和最新版本号关联
        query = db.query(WorkflowTemplate).join(
            subquery,
            (WorkflowTemplate.name == subquery.c.name) &
            (WorkflowTemplate.version == subquery.c.max_version)
        ).filter(
            WorkflowTemplate.deleted_at.is_(None)
        )

        # 筛选条件
        if name:
            query = query.filter(WorkflowTemplate.name.contains(name))

        if status:
            query = query.filter(WorkflowTemplate.status == status)

        # 总数
        total = query.count()

        # 排序
        if sort_by == "name":
            sort_field = WorkflowTemplate.name
        elif sort_by == "updated_at":
            sort_field = WorkflowTemplate.updated_at
        else:
            sort_field = WorkflowTemplate.created_at

        if order == "asc":
            query = query.order_by(asc(sort_field))
        else:
            query = query.order_by(desc(sort_field))

        # 分页
        templates = query.offset((page - 1) * page_size).limit(page_size).all()

        return total, templates

    @staticmethod
    def update_template(
        db: Session,
        template_id: int,
        template_update: WorkflowTemplateUpdate
    ) -> Tuple[bool, str, Optional[WorkflowTemplate]]:
        """
        更新模板（创建新版本）

        Args:
            db: 数据库会话
            template_id: 模板ID
            template_update: 更新数据

        Returns:
            (是否成功, 错误信息, 新版本模板对象)
        """
        try:
            # 获取当前模板
            current_template = WorkflowTemplateService.get_template(db, template_id)
            if not current_template:
                return False, "模板不存在", None

            # 准备新版本数据
            new_name = template_update.name if template_update.name is not None else current_template.name
            new_description = template_update.description if template_update.description is not None else current_template.description
            new_status = template_update.status if template_update.status is not None else current_template.status
            template_update = _rebuild_workflow_definition(template_update)
            # 工作流定义
            if template_update.workflow_definition is not None:
                workflow_def = template_update.workflow_definition.model_dump()
                new_workflow_def = json.dumps(workflow_def, ensure_ascii=False)

                # 验证工作流定义
                nodes = workflow_def.get("nodes", [])
                if not nodes:
                    return False, "工作流必须至少包含一个节点", None

                # 验证节点ID唯一性
                node_ids = [node.get("id") for node in nodes]
                if len(node_ids) != len(set(node_ids)):
                    return False, "节点ID必须唯一", None
            else:
                new_workflow_def = current_template.workflow_definition

            # 如果修改了名称，检查唯一性
            if new_name != current_template.name:
                existing = db.query(WorkflowTemplate).filter(
                    WorkflowTemplate.name == new_name,
                    WorkflowTemplate.deleted_at.is_(None)
                ).first()
                if existing:
                    return False, f"模板名称 '{new_name}' 已存在", None

            # 创建新版本
            new_version = WorkflowTemplate(
                name=new_name,
                description=new_description,
                workflow_definition=new_workflow_def,
                version=current_template.version + 1,
                parent_version_id=current_template.id,
                change_log=template_update.change_log or f"更新到版本 {current_template.version + 1}",
                status=new_status,
                created_by=current_template.created_by
            )

            db.add(new_version)
            db.commit()
            db.refresh(new_version)

            return True, "", new_version

        except Exception as e:
            db.rollback()
            return False, f"更新模板失败: {str(e)}", None

    @staticmethod
    def delete_template(
        db: Session,
        template_id: int
    ) -> Tuple[bool, str]:
        """
        删除模板（软删除）

        根据决策：允许删除有关联实例的模板，保留软删除记录供追溯

        Args:
            db: 数据库会话
            template_id: 模板ID

        Returns:
            (是否成功, 错误信息)
        """
        try:
            template = WorkflowTemplateService.get_template(db, template_id)
            if not template:
                return False, "模板不存在"

            # 检查是否有关联的实例（仅提示，不阻止删除）
            instances_count = db.query(WorkflowInstance).filter(
                WorkflowInstance.template_id == template_id,
                WorkflowInstance.deleted_at.is_(None)
            ).count()

            # 软删除
            template.deleted_at = datetime.now()
            db.commit()

            if instances_count > 0:
                return True, f"模板已删除（注意：有 {instances_count} 个关联实例，数据已保留供追溯）"
            else:
                return True, ""

        except Exception as e:
            db.rollback()
            return False, f"删除模板失败: {str(e)}"

    @staticmethod
    def get_version_history(
        db: Session,
        template_id: int
    ) -> List[VersionInfo]:
        """
        获取版本历史

        Args:
            db: 数据库会话
            template_id: 模板ID

        Returns:
            版本信息列表
        """
        # 获取当前模板
        current = WorkflowTemplateService.get_template(db, template_id)
        if not current:
            return []

        # 获取所有同名模板的历史版本
        versions = db.query(WorkflowTemplate).filter(
            WorkflowTemplate.name == current.name,
            WorkflowTemplate.deleted_at.is_(None)
        ).order_by(desc(WorkflowTemplate.version)).all()

        return [
            VersionInfo(
                id=v.id,
                parent_version_id=v.parent_version_id,
                version=v.version,
                created_at=v.created_at,
                updated_at=v.updated_at,
                change_log=v.change_log,
                created_by=v.created_by
            )
            for v in sorted(versions, key=lambda x: x.id)
        ]

    @staticmethod
    def get_template_by_name_and_version(
        db: Session,
        name: str,
        version: int
    ) -> Optional[WorkflowTemplate]:
        """
        根据模板名称和版本号查询模板

        Args:
            db: 数据库会话
            name: 模板名称
            version: 版本号

        Returns:
            模板对象或 None
        """
        return db.query(WorkflowTemplate).filter(
            WorkflowTemplate.name == name,
            WorkflowTemplate.version == version,
            WorkflowTemplate.deleted_at.is_(None)
        ).first()

    @staticmethod
    def get_version_diff(
        db: Session,
        template_id: int,
        version1: int,
        version2: int
    ) -> Tuple[bool, str, Optional[dict]]:
        """
        获取版本对比

        Args:
            db: 数据库会话
            template_id: 模板ID
            version1: 版本号1
            version2: 版本号2

        Returns:
            (是否成功, 错误信息, 对比数据)
        """
        # 获取当前模板
        current = WorkflowTemplateService.get_template(db, template_id)
        if not current:
            return False, "模板不存在", None

        # 获取两个版本
        v1 = db.query(WorkflowTemplate).filter(
            WorkflowTemplate.name == current.name,
            WorkflowTemplate.version == version1,
            WorkflowTemplate.deleted_at.is_(None)
        ).first()

        v2 = db.query(WorkflowTemplate).filter(
            WorkflowTemplate.name == current.name,
            WorkflowTemplate.version == version2,
            WorkflowTemplate.deleted_at.is_(None)
        ).first()

        if not v1 or not v2:
            return False, "指定的版本不存在", None

        # 返回对比数据
        return True, "", {
            "version1": {
                "id": v1.id,
                "version": v1.version,
                "workflow_definition": json.loads(v1.workflow_definition),
                "change_log": v1.change_log,
                "created_at": v1.created_at.isoformat() if v1.created_at else None
            },
            "version2": {
                "id": v2.id,
                "version": v2.version,
                "workflow_definition": json.loads(v2.workflow_definition),
                "change_log": v2.change_log,
                "created_at": v2.created_at.isoformat() if v2.created_at else None
            }
        }

    @staticmethod
    def rollback_to_version(
        db: Session,
        template_id: int,
        target_version: int
    ) -> Tuple[bool, str, Optional[WorkflowTemplate]]:
        """
        回滚到指定版本（创建新版本）

        Args:
            db: 数据库会话
            template_id: 模板ID
            target_version: 目标版本号

        Returns:
            (是否成功, 错误信息, 新版本模板对象)
        """
        try:
            # 获取当前模板
            current = WorkflowTemplateService.get_template(db, template_id)
            if not current:
                return False, "模板不存在", None

            # 获取目标版本
            target = db.query(WorkflowTemplate).filter(
                WorkflowTemplate.name == current.name,
                WorkflowTemplate.version == target_version,
                WorkflowTemplate.deleted_at.is_(None)
            ).first()

            if not target:
                return False, f"版本 {target_version} 不存在", None

            # 创建新版本（内容来自目标版本）
            new_version = WorkflowTemplate(
                name=current.name,
                description=target.description,
                workflow_definition=target.workflow_definition,
                version=current.version + 1,
                parent_version_id=current.id,
                change_log=f"回滚到版本 {target_version}",
                status=current.status,
                created_by=current.created_by
            )

            db.add(new_version)
            db.commit()
            db.refresh(new_version)

            return True, "", new_version

        except Exception as e:
            db.rollback()
            return False, f"回滚失败: {str(e)}", None

def _rebuild_workflow_definition(template: Union[WorkflowTemplateCreate, WorkflowTemplateUpdate]) -> Union[WorkflowTemplateCreate, WorkflowTemplateUpdate]:
    """
    重建工作流定义（如果需要）
    对节点以拓扑进行排序，确保定义的节点顺序合理

    Args:
        template: 工作流模板对象

    Returns:
        工作流定义字典
    """
    if template is None or template.workflow_definition is None:
        return template
    from collections import Counter, defaultdict, deque
    wf: WorkflowDefinition = template.workflow_definition
    pairs = defaultdict(list)
    indegree = Counter()
    for node in wf.nodes:
        pairs[node.id].append(node.next)
        indegree[node.next] += 1
    start_node = None
    for n in pairs:
        if indegree[n] == 0:
            start_node = n
            break
    ranks = dict()
    if not start_node:
        return wf
    bfs = deque([(start_node, 0)])
    while bfs:
        node, rank = bfs.popleft()
        ranks[node] = rank
        for nxt in pairs[node]:
            bfs.append((nxt, rank + 1))
    wf.nodes.sort(key=lambda x: ranks[x.id])
    return template